<template>
  <div class="virtual-list" ref="container" @scroll="handleScroll">
    <div class="phantom" :style="{ height: `${totalHeight}px` }"></div>
    <div class="list" :style="{ transform: `translateY(${offset}px)` }">
      <div v-for="item in visibleItems" :key="item.index" class="list-item">
        {{ item.value }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, watch } from 'vue'
/**
 container: 滚动容器。
 phantom: 占位元素，高度为所有元素的总高度，用于模拟滚动条。
 list: 实际渲染的列表项。
 visibleItems: 计算属性，根据滚动位置计算出当前可见的元素。 
 handleScroll: 处理滚动事件，更新 startIndex 和 endIndex。
 */
const props = defineProps({
  items: {
    type: Array,
    required: true,
  },
  itemHeight: {
    type: Number,
    required: true,
  },
})

const container = ref(null)
const startIndex = ref(0)
const endIndex = ref(20) // 初始可见项数
const visibleItems = computed(() => {
  return props.items
    .slice(startIndex.value, endIndex.value)
    .map((value, index) => ({
      index: startIndex.value + index,
      value,
    }))
})
const totalHeight = computed(() => props.items.length * props.itemHeight)
const offset = computed(() => startIndex.value * props.itemHeight)

const handleScroll = () => {
  const scrollTop = container.value.scrollTop
  startIndex.value = Math.floor(scrollTop / props.itemHeight)
  console.log(scrollTop, startIndex.value, props.itemHeight)

  endIndex.value = startIndex.value + 20 // 可见项数
}

onMounted(() => {
  container.value.addEventListener('scroll', handleScroll)
})

watch(
  () => props.items,
  () => {
    startIndex.value = 0
    endIndex.value = 20
  },
)
</script>

<style scoped>
.virtual-list {
  height: 400px;
  overflow-y: auto;
  position: relative;
  border: 1px solid #ccc;
}

.phantom {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: -1;
}

.list {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.list-item {
  padding: 10px;
  border-bottom: 1px solid #eee;
}
</style>
